package com.chainyoung.common.appcommon.hdwallet;

import android.content.Context;

import com.chainyoung.common.R;
import com.chainyoung.common.appcommon.entity.HdWalletCache;
import com.chainyoung.common.appcommon.hdwallet.WalletStoreManger;
import com.chainyoung.common.constant.LibConstant;
import com.chainyoung.common.utils.AppUtils;
import com.chainyoung.common.appcommon.entity.HdFullWallet;
import com.chainyoung.common.appcommon.entity.HdWallet;
import com.chainyoung.common.appcommon.entity.HdWallets;
import com.chainyoung.common.utils.SPUtil;
import com.chainyoung.common.utils.ToastUtil;
import com.chainyoung.common.view.kprogresshud.KProgressHUD;

import com.chainyoung.router.RouterConstants;
import com.chainyoung.router.RouterUtils;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;
import org.web3j.crypto.Wallet;
import org.web3j.crypto.WalletFile;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.ObjectMapperFactory;
import org.web3j.protocol.Web3j;
import org.web3j.utils.Numeric;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nonnull;

import io.github.novacrypto.bip39.MnemonicGenerator;
import io.github.novacrypto.bip39.SeedCalculator;
import io.github.novacrypto.bip39.Words;
import io.github.novacrypto.bip39.wordlists.English;
import io.github.novacrypto.hashing.Sha256;

import static com.chainyoung.common.appcommon.hdwallet.CoinConstant.ETH;


public class OwnWalletUtils extends WalletUtils {


    public static String generateNewWalletFile(
            String password, File destinationDirectory, boolean useFullScrypt)
            throws CipherException, IOException, InvalidAlgorithmParameterException,
            NoSuchAlgorithmException, NoSuchProviderException {

        ECKeyPair ecKeyPair = Keys.createEcKeyPair();
        return generateWalletFile(password, ecKeyPair, destinationDirectory, useFullScrypt);
    }


    public static String generateWalletFile(
            String password, ECKeyPair ecKeyPair, File destinationDirectory, boolean useFullScrypt)
            throws CipherException, IOException {

        WalletFile walletFile;
        if (useFullScrypt) {
            walletFile = Wallet.createStandard(password, ecKeyPair);
        } else {
            walletFile = Wallet.createLight(password, ecKeyPair);
        }

        String fileName = getWalletFileName(walletFile);
        File destination = new File(destinationDirectory, fileName);

        ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
        objectMapper.writeValue(destination, walletFile);

        return fileName;
    }

    private static String getWalletFileName(WalletFile walletFile) {
        return walletFile.getAddress();
    }


    public static void nem(String pwd) {
        final StringBuilder sb = new StringBuilder();
        byte[] entropy = new byte[Words.TWELVE.byteLength()];
        new SecureRandom().nextBytes(entropy);
        new MnemonicGenerator(English.INSTANCE)
                .createMnemonic(entropy, new MnemonicGenerator.Target() {
                    @Override
                    public void append(CharSequence string) {
                        sb.append(string);
                    }
                });
        String mnemonic = sb.toString();
        System.out.println("mnemonic:" + mnemonic);
        List mnemonicList = Arrays.asList(mnemonic.split(" "));
        byte[] seed = new SeedCalculator()
                .withWordsFromWordList(English.INSTANCE)
                .calculateSeed(mnemonicList, pwd);


        ECKeyPair ecKeyPair = ECKeyPair.create(Sha256.sha256(seed));
        String privateKey = ecKeyPair.getPrivateKey().toString(16);
        String publicKey = ecKeyPair.getPublicKey().toString(16);
        String address = Keys.getAddress(publicKey);
        System.out.println("privateKey:" + privateKey);
        System.out.println("publicKey:" + publicKey);
        System.out.println("address:" + address);

    }


    /**
     * 创建账户
     *
     * @param pwd
     * @return mnemonic :助记词
     * privateKey：私钥
     * publicKey：公钥
     * address：地址
     * accountFilePath：账户文件路径
     */
    public static HdWalletCache createNewAccount(String pwd, String coinType) {
        String path = "";
        final StringBuilder sb = new StringBuilder();
        byte[] entropy = new byte[Words.TWELVE.byteLength()];
        new SecureRandom().nextBytes(entropy);
        new MnemonicGenerator(English.INSTANCE)
                .createMnemonic(entropy, new MnemonicGenerator.Target() {
                    @Override
                    public void append(CharSequence string) {
                        sb.append(string);
                    }
                });
        String mnemonic = sb.toString();
        System.out.println("mnemonic:" + mnemonic);
        List mnemonicList = Arrays.asList(mnemonic.split(" "));
        byte[] seed = new SeedCalculator()
                .withWordsFromWordList(English.INSTANCE)
                .calculateSeed(mnemonicList, "");


        DeterministicSeed deterministicSeed = new DeterministicSeed(mnemonicList, seed, "", 0);
        DeterministicKeyChain deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build();
        String publicKey, privateKey, address;
        ECKeyPair ecKeyPair;

        switch (coinType) {
            case ETH:
                path = "m/44'/60'/0'/0/0";
                BigInteger privkeyeth = deterministicKeyChain.getKeyByPath(parsePath(path), true).getPrivKey();
                ecKeyPair = ECKeyPair.create(privkeyeth);
                publicKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPublicKey());
                privateKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPrivateKey());
                address = "0x" + Keys.getAddress(ecKeyPair);
                return saveWallet(mnemonic, privateKey, publicKey, address, coinType);
        }
        return null;
    }

    /**
     * 保存eth钱包（旧）
     */
  /*  private static HdWallet saveEthWallet(String mnemonic, String privateKey, String publicKey, String address, String coinType) {
        HdWallets wallets = new HdWallets();
        List<HdFullWallet> hdFullWalletList = new ArrayList<>();
        List<HdWallet> hdWallets = new ArrayList<>();
        HdFullWallet hdFullWallet = new HdFullWallet();
        HdWallet wallet = new HdWallet();
        wallet.setMnemonic(mnemonic);
        wallet.setPrivateKey(privateKey);
        wallet.setPublicKey(publicKey);
        wallet.setAddress(address);
        wallet.setCoinType(coinType);
        hdWallets.add(wallet);
        hdFullWallet.setCoinType(coinType);
        hdFullWallet.setMnemonic(mnemonic);
        hdFullWallet.setWallets(hdWallets);
        hdFullWalletList.add(hdFullWallet);
        wallets.setMnemonic(mnemonic);
        wallets.setWallets(hdFullWalletList);
        WalletStoreManger.setHdWallets(wallets);
        return wallet;
    }*/
    private static HdWalletCache saveWallet(String mnemonic, String privateKey, String publicKey, String address, String coinType) {
        HdWalletCache hdWalletCache = new HdWalletCache();
        hdWalletCache.setCoinType(coinType);
        hdWalletCache.setAddress(address);
        hdWalletCache.setMnemonic(mnemonic);
        hdWalletCache.setPrivateKey(privateKey);
        hdWalletCache.setPublicKey(publicKey);
        switch (coinType) {
            case ETH:
                hdWalletCache.setCnName(CoinConstant.ETH_CNNAME);
                hdWalletCache.setLogo(R.mipmap.icon_eth);
                break;
        }

        return hdWalletCache;

    }


    /**
     * 根据助记词导入
     *
     * @param pwd      ： 钱包密码
     * @param mnemonic ：助记词
     * @return
     */
    public static HdWalletCache importByMnemonic(Context context, String pwd, String mnemonic, String coinType) {

        //String filePath = AppUtils.getWalletPath();
        String path = "";
        List mnemonicList = Arrays.asList(mnemonic.split(" "));
        byte[] seed = new SeedCalculator()
                .withWordsFromWordList(English.INSTANCE)
                .calculateSeed(mnemonicList, "");


        DeterministicSeed deterministicSeed = new DeterministicSeed(mnemonicList, seed, "", 0);
        DeterministicKeyChain deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build();

        String publicKey = "", privateKey = "", address = "";
        ECKeyPair ecKeyPair = null;


        switch (coinType) {
            case ETH:
                //目前暂时解析第一个
                path = "m/44'/60'/0'/0/0";
                BigInteger privkeyeth = deterministicKeyChain.getKeyByPath(parsePath(path), true).getPrivKey();
                ecKeyPair = ECKeyPair.create(privkeyeth);
                publicKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPublicKey());
                privateKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPrivateKey());
                address = "0x" + Keys.getAddress(ecKeyPair);
                return saveWallet(mnemonic, privateKey, publicKey, address, coinType);
        }

        //创建钱包地址与密钥
      /*  String fileName = null;
        try {
            fileName = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(filePath), false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (fileName == null) {
            return;
        }*/
        return null;

       /* WalletStoreManger.setHdWallets(null);
        //保存钱包信息
        HdWallets wallets = new HdWallets();
        List<HdFullWallet> hdFullWalletList = new ArrayList<>();
        List<HdWallet> hdWallets = new ArrayList<>();
        HdFullWallet hdFullWallet = new HdFullWallet();
        HdWallet wallet = new HdWallet();
        wallet.setMnemonic(mnemonic);
        wallet.setPrivateKey(privateKey);
        wallet.setPublicKey(publicKey);
        wallet.setAddress(address);
        wallet.setCoinType(coinType);
        wallet.setLogo(R.mipmap.icon_eth);
        wallet.setCnName(CoinConstant.ETH_CNNAME);
        hdWallets.add(wallet);

        hdFullWallet.setCoinType(coinType);
        hdFullWallet.setMnemonic(mnemonic);
        hdFullWallet.setWallets(hdWallets);
        hdFullWalletList.add(hdFullWallet);

        wallets.setMnemonic(mnemonic);
        wallets.setWallets(hdFullWalletList);
        WalletStoreManger.setHdWallets(wallets);

        RouterUtils.getInstance()
                .build(RouterConstants.PATH_MAIN_MARKET_ACTIVITY)
                .withInt("page", 4)
                .withBoolean("isGetWallet", true)
                .navigation(context);*/

    }


    /**
     * 根据私钥导入
     *
     * @param context
     * @param pwd
     * @param walletPrivateKey
     * @param coinType
     */
    public static HdWalletCache importByPrivateKey(Context context, String pwd, String walletPrivateKey, String coinType) {

        String path = "";

        String publicKey = "", privateKey = "", address = "";
        ECKeyPair ecKeyPair = null;


        switch (coinType) {
            case ETH:
                //目前暂时解析第一个
                path = "m/44'/60'/0'/0/0";
                BigInteger privkeyeth = null;
                try {
                    privkeyeth = new BigInteger(walletPrivateKey.startsWith("0x") ? walletPrivateKey.substring(2) : walletPrivateKey, 16);
                } catch (Exception e) {
                    ToastUtil.showToast("请输入正确的私钥");
                    return null;

                }
                ecKeyPair = ECKeyPair.create(privkeyeth);
                publicKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPublicKey());
                privateKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPrivateKey());
                address = "0x" + Keys.getAddress(ecKeyPair);
                return saveWallet("", privateKey, publicKey, address, coinType);

        }

        //创建钱包地址与密钥
       /* String fileName = null;
        try {
            fileName = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(filePath), false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (fileName == null) {
            return;
        }
        String accountFilePath = filePath + File.separator + fileName;
        WalletStoreManger.setHdWallets(null);
        //保存钱包信息
        HdWallets wallets = new HdWallets();
        List<HdFullWallet> hdFullWalletList = new ArrayList<>();
        List<HdWallet> hdWallets = new ArrayList<>();
        HdFullWallet hdFullWallet = new HdFullWallet();
        HdWallet wallet = new HdWallet();
        wallet.setMnemonic("");
        wallet.setPrivateKey(privateKey);
        wallet.setPublicKey(publicKey);
        wallet.setAccountFilePath(accountFilePath);
        wallet.setAddress(address);
        wallet.setCoinType(coinType);
        wallet.setLogo(R.mipmap.icon_eth);
        wallet.setCnName(CoinConstant.ETH_CNNAME);
        hdWallets.add(wallet);

        hdFullWallet.setCoinType(coinType);
        hdFullWallet.setMnemonic("");
        hdFullWallet.setWallets(hdWallets);
        hdFullWalletList.add(hdFullWallet);

        wallets.setMnemonic("");
        wallets.setWallets(hdFullWalletList);
        WalletStoreManger.setHdWallets(wallets);*/

        return null;

       /* RouterUtils.getInstance()
                .build(RouterConstants.PATH_MAIN_MARKET_ACTIVITY)
                .withInt("page", 4)
                .withBoolean("isGetWallet", true)
                .navigation(context);*/

    }


    public static String getWalletAddr(String mnemonic, String coinType) {

        String addr = "";
        String path = "";
        List mnemonicList = Arrays.asList(mnemonic.split(" "));
        byte[] seed = new SeedCalculator()
                .withWordsFromWordList(English.INSTANCE)
                .calculateSeed(mnemonicList, "");


        DeterministicSeed deterministicSeed = new DeterministicSeed(mnemonicList, seed, "", 0);
        // DeterministicSeed deterministicSeed = new DeterministicSeed(mnemonicList, null, "", 0);
        DeterministicKeyChain deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build();

        String publicKey = "", privateKey = "", address = "";
        ECKeyPair ecKeyPair = null;


        switch (coinType) {
            case ETH:
                //目前暂时解析第一个
                path = "m/44'/60'/0'/0/0";
                BigInteger privkeyeth = deterministicKeyChain.getKeyByPath(parsePath(path), true).getPrivKey();
                ecKeyPair = ECKeyPair.create(privkeyeth);
                publicKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPublicKey());
                privateKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPrivateKey());
                addr = "0x" + Keys.getAddress(ecKeyPair);
                break;
        }

        return addr;
    }


    /**
     * 导出账户
     *
     * @param walletFilePath ： 账户完整路径，包括文件名
     * @param password       ： 密码
     * @return
     */
//    public Map export(String walletFilePath, String password) {
//        Map resultMap = new LinkedHashMap();
//        Credentials credentials = loadAccount(walletFilePath, password);
//        ECKeyPair ecKeyPair = credentials.getEcKeyPair();
//        boolean useFullScrypt = false;
//        WalletFile walletFile = null;
//        try {
//            if (useFullScrypt) {
//                walletFile = Wallet.createStandard(password, ecKeyPair);
//            } else {
//                walletFile = Wallet.createLight(password, ecKeyPair);
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        String fileNameEx = getWalletFileName(walletFile);
//        System.out.println("walletFile:" + JSON.toJSONString(walletFile));
//        System.out.println("fileNameEx:" + fileNameEx);
//        resultMap.put("walletFile", walletFile);
//        resultMap.put("fileName", fileNameEx);
//        return resultMap;
//    }
//    public static ShellWallet generateWallet(int coinType, int index, String name) throws Exception {
//        ShellWallet wallet = null;
//        if (words == null || words.size() != 12) {
//            throw new RuntimeException("please generateMnemonic first");
//        } else {
//            DeterministicSeed deterministicSeed = new DeterministicSeed(words, null, "", 0);
//            DeterministicKeyChain deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build();
//            //拼接路径 path = "m/44'/60'/0'/0/0"
//            String path = null;
//            if (coinType == BTC_COIN) {
//                path = "m/44'/0'/0'/0/" + index;
//                NetworkParameters networkParameters = null;
//                if (!BTC_TEST_NET) {
//                    networkParameters = MainNetParams.get();
//                } else {
//                    networkParameters = TestNet3Params.get();
//                    path = "m/44'/1'/0'/0/" + index;
//                }
//                BigInteger privkeybtc = deterministicKeyChain.getKeyByPath(parsePath(path), true).getPrivKey();
//                ECKey ecKey = ECKey.fromPrivate(privkeybtc);
//
//                //String publicKey = Numeric.toHexStringNoPrefixZeroPadded(new BigInteger(ecKey.getPubKey()), 66);
//                //LogUtils.d("pub1:" + publicKey );
//                String publicKey = ecKey.getPublicKeyAsHex();
//                //LogUtils.d("pub2:" + publicKey);
//                String privateKey = ecKey.getPrivateKeyEncoded(networkParameters).toString();
//                String address = ecKey.toAddress(networkParameters).toString();
//
//                // wallet = new ShellWallet(index, coinType, privateKey, publicKey, address, name);
//                //LogUtils.d(wallet.toString());
//            } else if (coinType == ETH_COIN) {
//                path = "m/44'/60'/0'/0/" + index;
//                BigInteger privkeyeth = deterministicKeyChain.getKeyByPath(parsePath(path), true).getPrivKey();
//                ECKeyPair ecKeyPair = ECKeyPair.create(privkeyeth);
//
//                String publicKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPublicKey());
//                String privateKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPrivateKey());
//                String address = "0x" + Keys.getAddress(ecKeyPair);
//
//                //wallet = new ShellWallet(index, coinType, privateKey, publicKey, address, name);
//                //LogUtils.d(wallet.toString());
//            }
//
//        }
//
//        return wallet;
//    }


    /**
     * The path is a human-friendly representation of the deterministic path. For example:
     * <p>
     * "m/44'/0'/0'/0/0"
     * <p>
     * Where a letter "'" means hardened key. Spaces are ignored.
     */
    public static List<ChildNumber> parsePath(@Nonnull String path) {
        String[] parsedNodes = path.replace("m", "").split("/");
        List<ChildNumber> nodes = new ArrayList<>();

        for (String n : parsedNodes) {
            n = n.replaceAll(" ", "");
            if (n.length() == 0) continue;
            boolean isHard = n.endsWith("'");
            if (isHard) n = n.substring(0, n.length() - 1);
            int nodeNumber = Integer.parseInt(n);
            nodes.add(new ChildNumber(nodeNumber, isHard));
        }

        return nodes;
    }

    /**
     * 获取 ETH keystore
     *
     * @param hdWallet
     * @return
     */
    public static String getAccountFilePath(HdWalletCache hdWallet) {
        String mnemonic = hdWallet.getMnemonic();
        String privKey = hdWallet.getPrivateKey();
        String filePath = AppUtils.getWalletPath();
        String path = "";
        /*List mnemonicList = Arrays.asList(mnemonic.split(" "));
        byte[] seed = new SeedCalculator()
                .withWordsFromWordList(English.INSTANCE)
                .calculateSeed(mnemonicList, "");

        DeterministicSeed deterministicSeed = new DeterministicSeed(mnemonicList, seed, "", 0);
        DeterministicKeyChain deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build();*/

        ECKeyPair ecKeyPair = null;


        switch (hdWallet.getCoinType()) {
            case ETH:
                //目前暂时解析第一个
                path = "m/44'/60'/0'/0/0";
                BigInteger privkeyeth = null;
                try {
                    privkeyeth = new BigInteger(privKey.startsWith("0x") ? privKey.substring(2) : privKey, 16);
                } catch (Exception e) {
                    return null;

                }
                ecKeyPair = ECKeyPair.create(privkeyeth);
                break;
        }

        //创建钱包地址与密钥
        String fileName = null;
        try {
            fileName = WalletUtils.generateWalletFile(SPUtil.getString(LibConstant.PIN_CODE), ecKeyPair, new File(filePath), false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (fileName == null) {
            return "";
        }
        String accountFilePath = filePath + File.separator + fileName;
        return accountFilePath;

    }


}
